﻿// 帐号相关接口API实现代码

#include "ApiHandle.h"

#include <mysql++/connection.h>
#include <mysql++/query.h>
#include <rapidjson/writer.h>

#include <cpptools/out.h>
#include <cpptools/time.hpp>
#include <cpptools/stringutil.h>
#include <cpptools/stdstring.hpp>

#include "node.h"
#include "main.h"
#include "PubOpt.pb.h"
#include "PubAcct.pb.h"
#include "PubConfig.h"
#include "PubProtocol.h"

static constexpr const uint64_t kInfo = ct::Out::flagValue({ (uint64_t)out::kApiAccInfo });
static constexpr const uint64_t kErr = ct::Out::flagValue({ (uint64_t)out::kApiAccErr });

template <typename Node, typename TypeClass, typename Less = std::less<Node>>
    void parseIdStr(const char *str, std::set<Node, Less> &nodeList);

// 帐号信息写入json，登陆成功后或token校验接口调用
static void accountInfoToJson(rapidjson::Writer<session_t::send_buf_t>& w, const Account::sptr_t& acc, uint64_t token) {
    w.StartObject();
    w.Key("token");
    w.Uint64(token);
    w.Key("account");
    w.String(acc->name().data(), (rapidjson::SizeType)acc->name().size());
    w.Key("alias");
    w.String(acc->alias().data(), (rapidjson::SizeType)acc->alias().size());
    w.Key("teamType");
    w.Uint(acc->teamType());
    w.Key("team");
    w.Uint(acc->team());
    w.Key("rate");
    w.Uint(acc->rate());
    w.Key("sex");
    w.Uint(acc->sex());
    w.Key("sys");
    w.Uint(acc->sys());
    w.Key("image");
    w.String(acc->image().data(), (rapidjson::SizeType)acc->image().size());
    w.Key("pms");
    w.StartArray();
    pms_list_t pmsList; // 防止各角色的权限重复, set过滤
    acc->pmsAll(pmsList);
    for (auto& itr : pmsList) {
        w.String(itr->name().data(), (rapidjson::SizeType)itr->name().size());
    }
    w.EndArray();
    if (acc->team() != 1 && acc->childTeams().size()) { // 非总组且有子孙分组列表
        w.Key("teams");
        w.StartArray();
        for (auto& itr : acc->childTeams()) {
            w.Uint(itr);
        }
        w.EndArray();
    }
    w.EndObject();
}

// 列表
void ApiHandle::accountList(uptr_t&& uptr) {
    rapidjson::Document::MemberIterator it;

    struct Info : public InfoBase {
        using InfoBase::InfoBase;
        string_t account, alias;
        uint64_t page, pageSize;
        int disable { 0 }, type { 0 };
        int64_t team { -1 };
        mysqlpp::StoreQueryResult res1, res2;
    };

    // Step: 必要参数
    if ((it = doc.FindMember("token")) == doc.MemberEnd() || it->value.GetType() != rapidjson::kNumberType) {
        ct::Out::debug("Error --> Account list, token.\n");
        return sendHead(pub::err::kParams);
    }
    uint64_t token = it->value.GetUint64();
    doc.RemoveMember(it);
    auto p = std::make_unique<Info>(std::forward<uptr_t>(uptr));

    if ((it = doc.FindMember("page")) == doc.MemberEnd() || it->value.GetType() != rapidjson::kNumberType
        || (p->page = it->value.GetUint64()) < 1) {
        ct::Out::debug("Error --> Account list, page.\n");
        return sendHead(pub::err::kParams);
    }
    doc.RemoveMember(it);

    if ((it = doc.FindMember("pageSize")) == doc.MemberEnd() || it->value.GetType() != rapidjson::kNumberType
        || (p->pageSize = it->value.GetUint64()) < 1) {
        ct::Out::debug("Error --> Account list, pageSize.\n");
        return sendHead(pub::err::kParams);
    }
    doc.RemoveMember(it);

    // Step: 可选参数
    if ((it = doc.FindMember("disable")) != doc.MemberEnd() && it->value.GetType() == rapidjson::kNumberType) {
        if (it->value.GetInt() != 1 && it->value.GetInt() != 0) { // 只能是0或1
            ct::Out::debug("Error --> Account list, disable.\n");
            return sendHead(pub::err::kParams);
        }
        p->disable = it->value.GetInt();
        doc.RemoveMember(it);
    }

    // 分组ID，分组详情中获取分组下帐号会提交此参数
    if ((it = doc.FindMember("team")) != doc.MemberEnd() && it->value.GetType() == rapidjson::kNumberType) {
        if (it->value.GetInt64() < 0) { // 只能正数
            ct::Out::debug("Error --> Account list, team.\n");
            return sendHead(pub::err::kParams);
        }
        p->team = it->value.GetInt64();
        doc.RemoveMember(it);
    }

    if ((it = doc.FindMember("type")) != doc.MemberEnd() && it->value.GetType() == rapidjson::kNumberType) {
        if (it->value.GetUint() < 1) { 
            return sendHead(pub::err::kParams);
        }
        p->type = it->value.GetUint();
        doc.RemoveMember(it);
    }

    if ((it = doc.FindMember("account")) != doc.MemberEnd() && it->value.GetType() == rapidjson::kStringType) {
        p->account.assign(it->value.GetString(), it->value.GetStringLength());
        doc.RemoveMember(it);
    }
    if ((it = doc.FindMember("alias")) != doc.MemberEnd() && it->value.GetType() == rapidjson::kStringType) {
        p->alias.assign(it->value.GetString(), it->value.GetStringLength());
        doc.RemoveMember(it);
    }

    // Step: 校验权限
    auto tokenOpt = Token::find(token);
    if (!tokenOpt) {
        return sendHead(pub::err::kToken);
    }
    auto& accSptr = (*tokenOpt)->accountSptr();

    if (!accSptr->pmsExist(Main::kPmsNameAccRead)) {
        return sendHead(pub::err::kPermission);
    }
    
    if (p->team < 0) { // 非查指定分组下帐号，表示获取当前分组帐号
        if (!(accSptr->sys() & protect::kAllPms)) { // 非超级管理员, 只允许查当前分组下帐号
            p->team = (int64_t)accSptr->team();
        }
    }

    // 数据库操作函数(任务线程)
    auto funcDb = [ p = std::move(p) ] () mutable {
        char buf[1024], tmp[400];
        int len, ret;
        enum Idx {
            ACCOUNT,
            TYPE,
            SYS,
            ALIAS,
            SEX,
            REPEAT
        };
    
        len = sprintf(buf, "select SQL_CALC_FOUND_ROWS `account`,`type`,`sys`,`alias`,`sex`,`repeat`"\
                      "  from `%s` where `disable`=%d and `id`>2", Main::kTableNameAccount, p->disable); // id>2过滤掉root和system

        // 条件
        if (p->type > 0) {
            ret = sprintf(buf+len, " and `type`=%d", p->type);
            len += ret;
        }
        if (p->team >= 0) {
            ret = sprintf(buf + len, " and `team`=%u", (uint32_t)p->team);
            len += ret;
        }
        if (!p->account.empty()) {
            ret = sprintf(buf + len, " and `account` LIKE '%%%s%%'", ct::StringUtil::encodeSqlPtr(p->account.c_str(), tmp));
            len += ret;
        }
        if (!p->alias.empty()) {
            ret = sprintf(buf + len, " and `alias` LIKE '%%%s%%'", ct::StringUtil::encodeSqlPtr(p->alias.c_str(), tmp));
            len += ret;
        }

        uint64_t offset = (p->page - 1) * p->pageSize;
        ret = sprintf(buf+len, " order by `id` desc limit %lu offset %lu;", p->pageSize, offset);
        len += ret;

        ret = sprintf(tmp, "SELECT FOUND_ROWS();"); // 第二条查询忽略limit的总条目数
        ct::Out::debug("sql:%s.\n", buf);

        auto db = Main::dbpool->grab();
        auto query = db->query();
        //outDebug("query1 before.\n");
        p->res1 = query.store(buf, len);
        //outDebug("query1 after.\n");
        if (!p->res1) { // 数据库读取错误
            ct::Out::flag(kErr, "Error --> Account list, err(%s), sql(%s).\n",
                          query.error(), buf);
            return Main::post([p = std::move(p)] { p->api->sendHead(pub::err::kInternal); });
        }
        query.clear();
        //outDebug("query2 before, res1Size:%zu.\n", p->res1.size());
        p->res2 = query.store(tmp, ret);
        //outDebug("query2 after.\n");
        if (!p->res2 || p->res2.num_rows() != 1) { // 数据库读取错误
            ct::Out::flag(kErr, "Error --> Account list, err(%s), sql(%s).\n",
                          query.error(), tmp);
            return Main::post([p = std::move(p)] { p->api->sendHead(pub::err::kInternal); });
        }
        //outDebug("query2 after, res2Size:%zu .\n", p->res2.size());
        query.clear();

        // 推送到主线程取数据(由于要在主线程取在线状态, 不能在任务线程构造JSON内容)
        auto funcMain = [p = std::move(p)] () mutable {
            auto& api = p->api;
            auto& res1 = p->res1;
            auto& row2 = p->res2[0]; // 总数
            //outDebug("row2Size: %zu .\n", row2.size());
            int total = (int)row2[0];
            //outDebug("total: %d.\n", total);
            rapidjson::Writer<buffer_t> w(api->buf);
            api->setHead((uint8_t)pub::err::kSuccess); // 协议头

            w.StartObject();
            w.Key("total");
            w.Int(total);
            w.Key("list");
            w.StartArray();

            for (auto& row : res1) {
                w.StartObject();
                w.Key("account");
                w.String(row[ACCOUNT].data(), (rapidjson::SizeType)row[ACCOUNT].length());
                w.Key("alias");
                w.String(row[ALIAS].data(), (rapidjson::SizeType)row[ALIAS].length());
                w.Key("type");
                w.Uint((unsigned int)row[TYPE]);
                w.Key("sex");
                w.Uint((unsigned int)row[SEX]);
                w.Key("repeat");
                w.Uint((unsigned int)row[REPEAT]);
                w.Key("protect");
                w.Uint((unsigned int)row[SYS]);
                w.Key("online"); // 在线状态
                auto accOpt = Account::find(row[ACCOUNT].data());
                w.Int(accOpt ? 1 : 0);
                w.EndObject();
            }

            w.EndArray();
            w.EndObject();
            outDebug("Account list construct success.\n");
            api->session->sendReponse(std::move(api->buf)); // 发送
        };

        // 推送到主线程发送
        Main::post(std::move(funcMain));
    };

    Main::tpool->push(std::move(funcDb));
}

void ApiHandle::accountDetail(uptr_t&& uptr) {
    struct Info : public InfoBase {
        using InfoBase::InfoBase;
        Account::sptr_t accSptr;
        string_t account;
        mysqlpp::StoreQueryResult res;
    };
    
    rapidjson::Document::MemberIterator it;

    // Step: 必要参数
    if ((it = doc.FindMember("token")) == doc.MemberEnd() || it->value.GetType() != rapidjson::kNumberType) {
        return sendHead(pub::err::kParams);
    }
    uint64_t token = it->value.GetUint64();
    doc.RemoveMember(it);

    // Step: 校验权限
    auto tokenOpt = Token::find(token);
    if (!tokenOpt) {
        return sendHead(pub::err::kToken);
    }
    auto p = std::make_unique<Info>(std::forward<uptr_t>(uptr));
    auto& accSptr = (*tokenOpt)->accountSptr();

    // 未指定名字或名字是自己表示查询自己
    if ((it = doc.FindMember("account")) != doc.MemberEnd() && it->value.GetType() == rapidjson::kStringType) {
        p->account.assign(it->value.GetString(), it->value.GetStringLength());
        doc.RemoveMember(it);

        if (accSptr->name() != p->account && !accSptr->pmsExist(Main::kPmsNameAccRead)) { // 查询别人需要权限
            return sendHead(pub::err::kPermission);
        }

    } else { // 查询自己
        p->account.assign(accSptr->name());
    }
    
    p->accSptr = accSptr;

    auto funcDb = [p = std::move(p)] () mutable {
        enum Idx {
            TYPE,
            SYS,
            DISABLE,
            REPEAT,
            SEX,
            RATE,
            CREATE_TIME,
            UPDATE_TIME,
            CREATE_ACCOUNT,
            ALIAS,
            PHONE,
            DESCRIBE,
            IMAGE,
            ROLE,
            PMS,
            TEAM,
            TEAM_TYPE,
        };

        char buf[1024];
        int len = sprintf(buf, "select `type`,`sys`,`disable`,`repeat`,`sex`,`rate`,`createTime`,`updateTime`,"\
                      "`createAccount`,`alias`,`phone`,`describe`,`image`,`role`,`pms`,`team`,`teamType`"\
                      "  from `%s` where `account`='%s'", Main::kTableNameAccount, p->account.c_str());
        ct::Out::debug("sql:%s.\n", buf);

        auto db = Main::dbpool->grab();
        auto query = db->query();
        p->res = query.store(buf, len);

        if (!p->res) { // 数据库读取错误
            ct::Out::flag(kErr, "Error --> Account detail db, err(%d)(%s), sql(%s).\n", 
                          query.errnum(), query.error(), buf);
            return Main::post([p = std::move(p)] { p->api->sendHead(pub::err::kDatabase); });
        }

        if (p->res.num_rows() == 0) { // 没有记录
            ct::Out::flag(kInfo, "Error --> Account detail db nodata: sql(%s).\n", buf);
            return Main::post([p = std::move(p)] { p->api->sendHead(pub::err::kNotFound); });
        }
        
        auto funcMain = [p = std::move(p)] {
            auto& api = p->api;
            auto& row = p->res[0];
            char tmp[200];
            int ret, sys = (int)row[SYS];
            auto& accSptr = p->accSptr;
            rapidjson::Writer<buffer_t> w(api->buf);
            api->setHead((uint8_t)pub::err::kSuccess); // 协议头
            
            w.StartObject();
            w.Key("account");
            w.String(p->account.data(), (rapidjson::SizeType)p->account.length());
            w.Key("alias");
            w.String(row[ALIAS].data(), (rapidjson::SizeType)row[ALIAS].length());
            w.Key("type");
            w.Uint((unsigned int)row[TYPE]);
            w.Key("protect");
            w.Int(sys);
            w.Key("sex");
            w.Uint((unsigned int)row[SEX]);
            w.Key("teamType");
            w.Uint((unsigned int)row[TEAM_TYPE]);
            w.Key("team");
            w.Uint((unsigned int)row[TEAM]);
            w.Key("repeat");
            w.Uint((unsigned int)row[REPEAT]);
            w.Key("disable");
            w.Uint((unsigned int)row[DISABLE]);
            w.Key("rate");
            w.Uint((unsigned int)row[RATE]);
            w.Key("createTime");
            w.String(row[CREATE_TIME].data(), (rapidjson::SizeType)row[CREATE_TIME].size());
            w.Key("updateTime");
            w.String(row[UPDATE_TIME].data(), (rapidjson::SizeType)row[UPDATE_TIME].size());
            w.Key("createAccount");
            w.String(row[CREATE_ACCOUNT].data(), (rapidjson::SizeType)row[CREATE_ACCOUNT].length());
            w.Key("phone");
            w.String(row[PHONE].data(), (rapidjson::SizeType)row[PHONE].length());
            w.Key("describe");
            w.String(row[DESCRIBE].data(), (rapidjson::SizeType)row[DESCRIBE].length());
            w.Key("image");
            w.String(row[IMAGE].data(), (rapidjson::SizeType)row[IMAGE].length());
            
            // 在线列表(token)
            w.Key("online");
            w.StartArray();
            auto accOpt = Account::find(p->account); // 找到目标帐号
            if (accOpt) { 
                auto& tokens = (*accOpt)->tokens(); // 该帐号下token值列表
                if (tokens.size()) {
                    uint32_t curtimesec = (uint32_t)time(nullptr);
                    std::vector<uint64_t> tokenVec(tokens.begin(), tokens.end()); // 先暂存出来,防止被删除迭代器失效
                    
                    for (auto& itr : tokenVec) { // 需要这样遍历,下面find可能会移除,析构,从而自动将帐号下token列表删除,迭代器破坏
                        auto tokenOpt = Token::find(itr); // 根据token值找token节点
                        if (tokenOpt) {
                            auto ptr = tokenOpt->get();
                            w.StartObject();
                            w.Key("token");
                            w.Uint64(ptr->token());
                            w.Key("on");
                            ct::Time::timestampToString((uint32_t)ptr->upTime(), tmp);
                            w.String(tmp, 19); // 转化后固定19长度
                            w.Key("times");
                            ret = ct::Time::durationToDhms((time_t)(curtimesec- ptr->upTime()), tmp);
                            w.String(tmp, (rapidjson::SizeType)ret);
                            w.Key("ip");
                            w.String(ptr->ip().c_str(), (rapidjson::SizeType)ptr->ip().size());
                            w.EndObject();
                        }
                    }
                }
            }
            w.EndArray();

            // 角色处理, 根据当前帐号角色组合
            w.Key("role");
            w.StartArray();
            std::set<role_t> targetRoleList; // 目标帐号角色存储,用于后续校验

            if (sys & protect::kAllRole) { // 目标是全角色帐号,导入所有角色ID
                for (auto& itr : Role::all) {
                    targetRoleList.insert(itr.second);
                }

            } else if (!row[ROLE].empty()) { // 解析目标角色
                parseIdStr<role_t, Role>(row[ROLE].data(), targetRoleList);
            }

            std::vector<role_t> thisRoleList; // 本帐号角色列表存储指针(单线程没问题,后面要排序追求效率)
            if (accSptr->sys() & protect::kAllRole) { // 此帐号为全角色帐号
                thisRoleList.reserve(Role::all.size());
                for (auto& itr : Role::all) {
                    thisRoleList.push_back(itr.second); // map中自动按ID排序
                }
            } else {
                thisRoleList.insert(thisRoleList.end(), accSptr->role().begin(), accSptr->role().end());
            }

            // 两个列表比较
            for (auto& itr : thisRoleList) {
                w.StartObject();
                w.Key("id");
                w.Uint(itr->id());
                w.Key("name");
                w.String(itr->name().c_str(), (rapidjson::SizeType)itr->name().size());
                w.Key("set");
                w.Int(targetRoleList.find(itr) == targetRoleList.end() ? 0 : 1); // 查找在目标列表中是否存在
                w.Key("pms");
                w.StartArray(); // 此角色下权限列表
                for (auto& pms : itr->pms()) {
                    w.Uint(pms->id());
                }
                w.EndArray();
                w.EndObject();
            }

            w.EndArray();

            // 权限处理,根据当前帐号权限组合
            w.Key("pms");
            w.StartArray();
            
            pms_list_t targetPmsList; // 目标帐号权限存储,用于后续校验
            if (sys & protect::kAllPms) { // 目标是全权限帐号,导入所有权限ID
                for (auto& itr : Permission::all) {
                    targetPmsList.insert(itr.second);
                }
            } else if (!row[PMS].empty()) { // 解析目标权限
                parseIdStr<pms_t, Permission, PmsComp>(row[PMS].data(), targetPmsList);
            }

            pms_list_t thisPmsList;
            accSptr->pmsAll(thisPmsList); // 本帐号所有权限列表，包含角色里的

            // 两个列表比较
            pms_list_t::iterator targetIt;
            for (auto& itr : thisPmsList) {
                w.StartObject();
                w.Key("id");
                w.Uint(itr->id());
                w.Key("alias");
                w.String(itr->alias().c_str(), (rapidjson::SizeType)itr->alias().size());
                w.Key("set");
                targetIt = targetPmsList.find(itr);
                ret = targetIt == targetPmsList.end() ? 0 : 1; // 查找在目标列表中是否存在
                w.Int(ret); 
                if (ret) { // 存在此权限，先从目标权限列表移除
                    targetPmsList.erase(targetIt);
                }
                w.EndObject();
            }

            w.EndArray(); // pms array

            w.EndObject(); // 最后
            api->session->sendReponse(std::move(api->buf));// 发送
        };

        Main::post(std::move(funcMain));
    };

    Main::tpool->push(std::move(funcDb));
}

// 创建
void ApiHandle::accountCreate(uptr_t&& uptr) {
    struct Info : public InfoBase {
        using InfoBase::InfoBase;
        string_t account, password, alias, phone, image, describe;
        ct::StdString pmsStr, roleStr;
        uint32_t sex { 0 }, repeat { 0 }, rate { 1 }, team, teamType;
        Account::sptr_t accSptr;
    };

    rapidjson::Document::MemberIterator it;
    auto p = std::make_unique<Info>(std::forward<uptr_t>(uptr));

    // Step: 必要参数
    if ((it = doc.FindMember("token")) == doc.MemberEnd() || it->value.GetType() != rapidjson::kNumberType) {
        ct::Out::debug("Error --> Account create, token.\n");
        return sendHead(pub::err::kParams);
    }
    uint64_t token = it->value.GetUint64();
    doc.RemoveMember(it);
    
    auto tokenOpt = Token::find(token); // 校验token和权限
    if (!tokenOpt) {
        return sendHead(pub::err::kToken);
    }
    auto& accSptr = (*tokenOpt)->accountSptr();
    if (!accSptr->pmsExist(Main::kPmsNameAccWrite)) {
        return sendHead(pub::err::kPermission);
    }

    if ((it = doc.FindMember("account")) == doc.MemberEnd() || it->value.GetType() != rapidjson::kStringType
        || it->value.GetStringLength() > Account::kNameMaxLen || it->value.GetStringLength() < 1
        || !ct::StringUtil::allow(it->value.GetString(), "-_", it->value.GetStringLength(), 2)) { // 只允许字母数字,下划线中划线
        ct::Out::debug("Error --> Account create, account.\n");
        return sendHead(pub::err::kParams);
    }
    p->account.assign(it->value.GetString(), it->value.GetStringLength());
    doc.RemoveMember(it);

    if ((it = doc.FindMember("password")) == doc.MemberEnd() || it->value.GetType() != rapidjson::kStringType
        || it->value.GetStringLength() != 32) { // md5必须32位
        ct::Out::debug("Error --> Account create, password.\n");
        return sendHead(pub::err::kParams);
    }
    p->password.assign(it->value.GetString(), it->value.GetStringLength());
    doc.RemoveMember(it);

    // Step: 可选参数
    if ((it = doc.FindMember("alias")) != doc.MemberEnd() && it->value.GetType() == rapidjson::kStringType) {
        if (it->value.GetStringLength() > Account::kAliasMaxLen) {
            ct::Out::debug("Error --> Account create, alias len.\n");
            return sendHead(pub::err::kParams);
        }
        p->alias.assign(it->value.GetString(), it->value.GetStringLength());
        doc.RemoveMember(it);
    }
    if ((it = doc.FindMember("phone")) != doc.MemberEnd() && it->value.GetType() == rapidjson::kStringType) {
        if (it->value.GetStringLength() > Account::kPhoneMaxLen) {
            ct::Out::debug("Error --> Account create, phone len.\n");
            return sendHead(pub::err::kParams);
        }
        p->phone.assign(it->value.GetString(), it->value.GetStringLength());
        doc.RemoveMember(it);
    }
    if ((it = doc.FindMember("image")) != doc.MemberEnd() && it->value.GetType() == rapidjson::kStringType) {
        if (it->value.GetStringLength() > Account::kImageMaxLen) {
            ct::Out::debug("Error --> Account create, image len.\n");
            return sendHead(pub::err::kParams);
        }
        p->image.assign(it->value.GetString(), it->value.GetStringLength());
        doc.RemoveMember(it);
    }
    if ((it = doc.FindMember("describe")) != doc.MemberEnd() && it->value.GetType() == rapidjson::kStringType) {
        if (it->value.GetStringLength() > Account::kDescMaxLen) {
            ct::Out::debug("Error --> Account create, describe len.\n");
            return sendHead(pub::err::kParams);
        }
        p->describe.assign(it->value.GetString(), it->value.GetStringLength());
        doc.RemoveMember(it);
    }
    if ((it = doc.FindMember("repeat")) != doc.MemberEnd() && it->value.GetType() == rapidjson::kNumberType) {
        p->repeat = it->value.GetUint();
        doc.RemoveMember(it);
    }
    if ((it = doc.FindMember("sex")) != doc.MemberEnd() && it->value.GetType() == rapidjson::kNumberType) {
        p->sex = it->value.GetUint();
        doc.RemoveMember(it);
    }
    if ((it = doc.FindMember("rate")) != doc.MemberEnd() && it->value.GetType() == rapidjson::kNumberType) {
        p->rate = it->value.GetUint();
        doc.RemoveMember(it);
    }
    
    if ((it = doc.FindMember("role")) != doc.MemberEnd() && it->value.GetType() == rapidjson::kArrayType) {
        auto array = it->value.GetArray();
        p->roleStr.reserve(200);
        rapidjson::Writer<ct::StdString> w(p->roleStr);
        w.StartArray();
        for (auto& id : array) {
            if (id.GetType() != rapidjson::kNumberType) {
                continue;
            }
            auto roleNode = Role::find(id.GetUint()); // 全局权限列表查找是否有此权限
            if (roleNode && accSptr->roleExistProtect(roleNode)) { // 有此角色
                w.Uint(id.GetUint());
            }
        }
        doc.RemoveMember(it);
        w.EndArray();
    }

    if ((it = doc.FindMember("pms")) != doc.MemberEnd() && it->value.GetType() == rapidjson::kArrayType) {
        auto array = it->value.GetArray();
        p->pmsStr.reserve(200);
        rapidjson::Writer<ct::StdString> w(p->pmsStr);
        w.StartArray();
        for (auto& id : array) {
            if (id.GetType() != rapidjson::kNumberType) {
                continue;
            }
            auto pmsNode = Permission::find(id.GetUint()); // 全局权限列表查找是否有此权限
            if (pmsNode && accSptr->pmsExistProtect(pmsNode)) { // 本帐号有此权限
                w.Uint(id.GetUint());
            }
        }
        w.EndArray();
    }

    p->team = accSptr->team(); // 默认创建者帐号一个分组ID
    p->teamType = accSptr->teamType();
    p->accSptr = accSptr;

    // 任务线程函数, 处理数据库
    auto funcDb = [p = std::move(p)] () mutable {
        auto db = Main::dbpool->grab();
        auto query = db->query();

        query << "insert into `"
            << Main::kTableNameAccount
            << "` (`account`,`repeat`,`sex`,`team`,`teamType`,`rate`,`alias`,`createAccount`,"
            << "`password`,`phone`,`image`,`describe`,`role`,`pms`) values ("
            << mysqlpp::quote << p->account 
            << "," << p->repeat 
            << "," << p->sex 
            << "," << p->team 
            << "," << p->teamType 
            << "," << p->rate
            << "," << mysqlpp::quote << p->alias
            << "," << mysqlpp::quote << p->accSptr->name()
            << "," << mysqlpp::quote << p->password
            << "," << mysqlpp::quote << p->phone
            << "," << mysqlpp::quote << p->image
            << "," << mysqlpp::quote << p->describe
            << "," << mysqlpp::quote << p->roleStr
            << "," << mysqlpp::quote << p->pmsStr
            << ");";
        std::cout << "SQL:" << query.str() << std::endl;
        //mysqlpp::Transaction trans(*db);
        auto res = query.execute();

        if (!res || res.rows() != 1) {
            ct::Out::flag(kErr, "Error --> Create db failed, err(%d)(%s).\n",
                          query.errnum(), query.error());
            // 回复错误，1062为重复主键错误，表示重名了
            uint8_t err = query.errnum() == 1062 ? pub::err::kExist : pub::err::kInternal;
            return Main::post([p = std::move(p), err] { p->api->sendHead(err); });
        }

    #if 0
        // 直接处理日志
        auto& accSptr = p->accSptr;
        query.reset();
        auto now = std::chrono::floor<std::chrono::seconds>(std::chrono::system_clock::now());
        //auto now = std::chrono::system_clock::now();
        query << "insert into `"
            << pub::kTableNameLogOpt
            << "` (`dt`,`team`,`opt`,`name`,`acct`) values ("
            << mysqlpp::quote << std::format("{:%Y-%m-%d %H:%M:%S}", now)
            << "," << accSptr->team()
            << "," << p->api->opt()
            << "," << mysqlpp::quote << p->account
            << "," << mysqlpp::quote << accSptr->name()
            << ");";
        std::cout << "LogSQL:" << query.str() << std::endl;
        res = query.execute();
        if (!res || res.rows() != 1) {
            ct::Out::flag(kErr, "Error --> Insert log failed, err(%d)(%s).\n",
                          query.errnum(), query.error());
            return Main::post([p = std::move(p)] { p->api->sendHead(pub::err::kInternal); });
        }

        trans.commit(); // 提交事务

        Main::post([p = std::move(p)] { p->api->sendHead(pub::err::kSuccess); }); // 回复成功
    #endif

        auto funcMain = [p = std::move(p)] {
            auto& api = p->api;
            auto& accSptr = p->accSptr;

            // 日志
            std::string remark;
            remark.reserve(2048);
            char tmp[4096];
            int ret;

            if (!p->alias.empty()) { // p里面name和alias已经被move到pmsSptr里了
                remark += "(alias:" + p->alias + ")";
            }
            if (!p->phone.empty()) {
                remark += "(phone:" + p->phone + ")";
            }
            if (!p->image.empty()) {
                remark += "(image:" + p->image + ")";
            }
            ret = sprintf(tmp, "(repeat:%u)(rate:%u)(sex:%u)(role:%s)(permission:%s)", 
                          p->repeat, p->rate, p->sex, p->roleStr.data(), p->pmsStr.data());
            remark.append(tmp, ret);

            pub::PbOpt pb;
            pb.set_account(accSptr->name());
            pb.set_time((uint32_t)time(nullptr));
            pb.set_opt(api->opt());
            pb.set_remark(remark);
            pb.set_team(accSptr->team());
            pb.add_target_name(p->account);
            Main::Main::sendLog(pb, pub::log::kOpt);

            api->sendHead(pub::err::kSuccess); // 回复
        };

        Main::post(std::move(funcMain));
    };

    // 新线程中处理数据库操作
    Main::tpool->push(std::move(funcDb)); // 线程池中处理
}

// 更新帐号
void ApiHandle::accountUpdate(uptr_t&& uptr) {
    enum Flag : uint32_t {
        PASSWORD = 1, PMS = 2, REPEAT = 4, RATE = 8, TEAM = 16, ALIAS = 32, 
        PHONE = 64, IMAGE = 128, SEX = 256, DESCRIBE = 512, ROLE = 1024, TEAM_TYPE = 2048,
    };
    struct Info : public InfoBase {
        using InfoBase::InfoBase;
        string_t account, password, oldPassword, alias, phone, image, describe;
        ct::StdString pmsStr, roleStr; // 权限, 角色列表字符串形式, 用于修改数据库
        pms_list_t pms; // 权限列表map，用于更新缓存
        role_list_t role; // 角色列表map, 用于更新缓存
        uint32_t sex, repeat, rate, team, teamType, flag { 0 };
        bool self { true };
        Account::sptr_t accSptr, targetAccSptr;
    };
    
    rapidjson::Document::MemberIterator it;
    auto p = std::make_unique<Info>(std::forward<uptr_t>(uptr));

    if ((it = doc.FindMember("token")) == doc.MemberEnd() || it->value.GetType() != rapidjson::kNumberType) {
        ct::Out::debug("Error --> Account update, token.\n");
        return sendHead(pub::err::kParams);
    }
    uint64_t token = it->value.GetUint64();
    doc.RemoveMember(it);
    auto tokenOpt = Token::find(token); // 校验权限
    if (!tokenOpt) {
        return sendHead(pub::err::kToken);
    }
    auto& accSptr = (*tokenOpt)->accountSptr();

    if ((it = doc.FindMember("account")) != doc.MemberEnd() && it->value.GetType() == rapidjson::kStringType) { // 目标
        if (accSptr->name() != it->value.GetString()) { // 不等于自己
            p->self = false;
            
            if (!accSptr->pmsExist(Main::kPmsNameAccWrite)) { // 修改其它帐号需要写权限
                return sendHead(pub::err::kPermission);
            }
        }
        p->account.assign(it->value.GetString(), it->value.GetStringLength());
        doc.RemoveMember(it);
    } else {
        p->account = accSptr->name();
    }

    // 可选参数校验
    if ((it = doc.FindMember("sex")) != doc.MemberEnd() && it->value.GetType() == rapidjson::kNumberType) {
        int ret = it->value.GetInt();
        if (ret < 0 || ret > 2) {
            ct::Out::debug("Error --> Account update, sex.\n");
            return sendHead(pub::err::kParams);
        }
        p->flag |= SEX;
        p->sex = (uint32_t)ret;
        doc.RemoveMember(it);
    }

    if ((it = doc.FindMember("alias")) != doc.MemberEnd() && it->value.GetType() == rapidjson::kStringType) {
        if (it->value.GetStringLength() > Account::kAliasMaxLen) {
            ct::Out::debug("Error --> Account update, alias len.\n");
            return sendHead(pub::err::kParams);
        }
        p->flag |= ALIAS;
        p->alias.assign(it->value.GetString(), it->value.GetStringLength());
        doc.RemoveMember(it);
    }

    if ((it = doc.FindMember("phone")) != doc.MemberEnd() && it->value.GetType() == rapidjson::kStringType) {
        if (it->value.GetStringLength() > Account::kPhoneMaxLen) {
            ct::Out::debug("Error --> Account update, phone len.\n");
            return sendHead(pub::err::kParams);
        }
        p->flag |= PHONE;
        p->phone.assign(it->value.GetString(), it->value.GetStringLength());
        doc.RemoveMember(it);
    }

    if ((it = doc.FindMember("image")) != doc.MemberEnd() && it->value.GetType() == rapidjson::kStringType) {
        if (it->value.GetStringLength() > Account::kImageMaxLen) {
            ct::Out::debug("Error --> Account update, image len.\n");
            return sendHead(pub::err::kParams);
        }
        p->flag |= IMAGE;
        p->image.assign(it->value.GetString(), it->value.GetStringLength());
        doc.RemoveMember(it);
    }

    if ((it = doc.FindMember("describe")) != doc.MemberEnd() && it->value.GetType() == rapidjson::kStringType) {
        if (it->value.GetStringLength() > Account::kDescMaxLen) {
            ct::Out::debug("Error --> Account update, describe len.\n");
            return sendHead(pub::err::kParams);
        }
        p->flag |= DESCRIBE;
        p->describe.assign(it->value.GetString(), it->value.GetStringLength());
        doc.RemoveMember(it);
    }

    if ((it = doc.FindMember("password")) != doc.MemberEnd() && it->value.GetType() == rapidjson::kStringType) {
        if (it->value.GetStringLength() != 32) {
            ct::Out::debug("Error --> Account update, password len.\n");
            return sendHead(pub::err::kParams);
        }
        p->password.assign(it->value.GetString(), it->value.GetStringLength());
        doc.RemoveMember(it);
        if (p->self) { // 修改自身，需要额外提供旧密码
            if ((it = doc.FindMember("oldPassword")) == doc.MemberEnd() 
                || it->value.GetType() != rapidjson::kStringType || it->value.GetStringLength() != 32) {
                ct::Out::debug("Error --> Account update, old password.\n");
                return sendHead(pub::err::kParams);
            }
            p->oldPassword.assign(it->value.GetString(), it->value.GetStringLength());
            doc.RemoveMember(it);
        }
        p->flag |= PASSWORD;
    }

    if (!p->self) { // 非自身
        if ((it = doc.FindMember("repeat")) != doc.MemberEnd() && it->value.GetType() == rapidjson::kNumberType) {
            int ret = it->value.GetInt();
            if (ret < 0 || ret > 1) {
                ct::Out::debug("Error --> Account update, repeat.\n");
                return sendHead(pub::err::kParams);
            }
            p->flag |= REPEAT;
            p->repeat = (uint32_t)ret;
            doc.RemoveMember(it);
        }
        if ((it = doc.FindMember("rate")) != doc.MemberEnd() && it->value.GetType() == rapidjson::kNumberType) {
            int ret = it->value.GetInt();
            if (ret < 1 || ret > 10000) {
                ct::Out::debug("Error --> Account update, rate.\n");
                return sendHead(pub::err::kParams);
            }
            p->flag |= RATE;
            p->rate = (uint32_t)ret;
            doc.RemoveMember(it);
        }
        if ((it = doc.FindMember("team")) != doc.MemberEnd() && it->value.GetType() == rapidjson::kNumberType) {
            int ret = it->value.GetInt();
            if (ret < 0) {
                ct::Out::debug("Error --> Account update, team.\n");
                return sendHead(pub::err::kParams);
            }
            if (accSptr->team() != 1 && ret == 1) { // 当前帐号非总组不能将目标改为总组
                ct::Out::debug("Error --> Account update, team.\n");
                return sendHead(pub::err::kPermission);
            }
            p->flag |= TEAM;
            p->team = (uint32_t)ret;
            doc.RemoveMember(it);
        }
        if ((it = doc.FindMember("teamType")) != doc.MemberEnd() && it->value.GetType() == rapidjson::kNumberType) {
            int ret = it->value.GetInt();
            if (ret < 1 || ret > 2) {
                ct::Out::debug("Error --> Account update, team type.\n");
                return sendHead(pub::err::kParams);
            }
            p->flag |= TEAM_TYPE;
            p->teamType = (uint32_t)ret;
            doc.RemoveMember(it);
        }

        if ((it = doc.FindMember("role")) != doc.MemberEnd() && it->value.GetType() == rapidjson::kArrayType) {
            auto arr = it->value.GetArray();
            rapidjson::Writer<ct::StdString> w(p->roleStr);
            uint32_t roleId;
            role_t roleNode;

            w.StartArray();

            for (auto& itr : arr) {
                if (itr.GetType() != rapidjson::kNumberType) {
                    continue;
                }
                roleId = itr.GetUint();
                roleNode = Role::find(roleId); // 全局权限列表是否有些权限
                if (roleNode && accSptr->roleExistProtect(roleNode)) {
                    w.Uint(roleId);
                    p->role.insert(roleNode); // 加入新的列表中,后面可能要更新缓存
                }
            }

            w.EndArray();
            p->flag |= ROLE;
        }

        if ((it = doc.FindMember("pms")) != doc.MemberEnd() && it->value.GetType() == rapidjson::kArrayType) {
            auto arr = it->value.GetArray();
            rapidjson::Writer<ct::StdString> w(p->pmsStr);
            uint32_t pmsId;
            pms_t pmsNode;
            
            w.StartArray();

            for (auto& itr : arr) {
                if (itr.GetType() != rapidjson::kNumberType) {
                    continue;
                }
                pmsId = itr.GetUint();
                pmsNode = Permission::find(pmsId); // 全局权限列表是否有些权限
                if (pmsNode && accSptr->pmsExistProtect(pmsNode)) {
                    w.Uint(pmsId);
                    p->pms.insert(pmsNode); // 加入新的列表中,后面可能要更新缓存
                }
            }

            w.EndArray();
            p->flag |= PMS;
        }
    }

    if (!p->flag) { // 一个参数也没改
        ct::Out::debug("Error --> account update, modify 0.\n");
        return sendHead(pub::err::kSuccess);
    }

    p->accSptr = accSptr;

    // 数据库操作函数
    auto funcDb = [p = std::move(p)] () mutable {
        char buf[4096], tmp[600];
        int ret, len = sprintf(buf, "update `%s` set ", Main::kTableNameAccount);

        if (p->flag & (uint32_t)PHONE) {
            ret = sprintf(buf + len, "`phone`='%s',", ct::StringUtil::encodeSqlPtr(p->phone.c_str(), tmp));
            len += ret;
        }
        if (p->flag & (uint32_t)ALIAS) {
            ret = sprintf(buf + len, "`alias`='%s',", ct::StringUtil::encodeSqlPtr(p->alias.c_str(), tmp));
            len += ret;
        }
        if (p->flag & (uint32_t)IMAGE) {
            ret = sprintf(buf + len, "`image`='%s',", ct::StringUtil::encodeSqlPtr(p->image.c_str(), tmp));
            len += ret;
        }
        if (p->flag & (uint32_t)DESCRIBE) {
            ret = sprintf(buf + len, "`describe`='%s',", ct::StringUtil::encodeSqlPtr(p->describe.c_str(), tmp));
            len += ret;
        }
        if (p->flag & (uint32_t)PASSWORD) {
            ret = sprintf(buf + len, "`password`='%s',", ct::StringUtil::encodeSqlPtr(p->password.c_str(), tmp));
            len += ret;
        }
        if (p->flag & (uint32_t)ROLE) {
            ret = sprintf(buf + len, "`role`='%s',", p->roleStr.c_str());
            len += ret;
        }
        if (p->flag & (uint32_t)PMS) {
            ret = sprintf(buf + len, "`pms`='%s',", p->pmsStr.c_str());
            len += ret;
        }
        if (p->flag & (uint32_t)REPEAT) {
            ret = sprintf(buf + len, "`repeat`=%u,", p->repeat);
            len += ret;
        }
        if (p->flag & (uint32_t)RATE) {
            ret = sprintf(buf + len, "`rate`=%u,", p->rate);
            len += ret;
        }
        if (p->flag & (uint32_t)TEAM_TYPE) {
            ret = sprintf(buf + len, "`teamType`=%u,", p->teamType);
            len += ret;
        }
        if (p->flag & (uint32_t)TEAM) {
            ret = sprintf(buf + len, "`team`=%u,", p->team);
            len += ret;
        }
        if (p->flag & (uint32_t)SEX) {
            ret = sprintf(buf + len, "`sex`=%u,", p->sex);
            len += ret;
        }

        ret = sprintf(buf + len, "`updateTime`=now() where `account`='%s'", p->account.c_str()); // 必须没有保护才可以
        len += ret;

        if (!p->self) { // 非修改自身, 必须没有保护
            ret = sprintf(buf + len, " and !(`sys` & %hhu)", protect::kNoMod);
            len += ret;
        } else if ((p->flag & (uint32_t)PASSWORD)) { // 修改自身并且改密码
            ret = sprintf(buf + len, " and `password`='%s'", p->oldPassword.c_str()); // 加上旧密码
            len += ret;
        }
        buf[len++] = ';';
        buf[len] = 0;
        
        auto db = Main::dbpool->grab();
        auto query = db->query();
        auto res = query.execute(buf, len);
        ct::Out::debug("sql:%s, rows:%d.\n", buf, (int)res.rows());

        if (!res || res.rows() != 1) {
            ct::Out::flag(kErr, "Error --> Update account db, err(%d)(%s), sql(%s).\n",
                          query.errnum(), query.error(), buf);
            return Main::post([p = std::move(p), 
                              err = p->self ? pub::err::kAccPwd : pub::err::kPermission] { 
                              p->api->sendHead(err); });
        }

        // 主线程操作函数, 节点在线则更新缓存, 回复成功
        auto funcMain = [p = std::move(p)] {
            auto& api = p->api;
            auto& accSptr = p->accSptr;
            std::string remark;
            remark.reserve(800);
            char tmp[100];
            int ret;

            if (p->flag & (uint32_t)ALIAS) {
                remark += "(alias:" + p->alias + ")";
            }
            if (p->flag & (uint32_t)PHONE) {
                remark += "(phone:" + p->phone + ")";
            }
            if (p->flag & (uint32_t)IMAGE) {
                remark += "(image:" + p->image + ")";
            }
            if (p->flag & (uint32_t)PASSWORD) {
                remark += "(password)";
            }
            if (p->flag & (uint32_t)REPEAT) {
                ret = sprintf(tmp, "(repeat:%u)", p->repeat);
                remark.append(tmp, ret);
            }
            if (p->flag & (uint32_t)RATE) {
                ret = sprintf(tmp, "(rate:%u)", p->rate);
                remark.append(tmp, ret);
            }
            if (p->flag & (uint32_t)TEAM_TYPE) {
                ret = sprintf(tmp, "(teamType:%u)", p->teamType);
                remark.append(tmp, ret);
            }
            if (p->flag & (uint32_t)TEAM) {
                ret = sprintf(tmp, "(team:%u)", p->team);
                remark.append(tmp, ret);
            }
            if (p->flag & (uint32_t)SEX) {
                ret = sprintf(tmp, "(sex:%u)", p->sex);
                remark.append(tmp, ret);
            }
            if (p->flag & (uint32_t)ROLE) {
                remark += "(role:" + (std::string)p->roleStr + ")";
            }
            if (p->flag & (uint32_t)PMS) { 
                remark += "(permission:" + (std::string)p->pmsStr + ")";
            }
            if (p->flag & (uint32_t)DESCRIBE) {
                remark += "(desc:" + p->describe + ")";
            }

            pub::PbOpt pb;
            pb.set_account(accSptr->name());
            pb.set_time((uint32_t)time(nullptr));
            pb.set_opt(api->opt());
            pb.set_remark(remark);
            pb.set_team(accSptr->team());
            pb.add_target_name(p->account);
            Main::Main::sendLog(pb, pub::log::kOpt);

            auto accOpt = Account::find(p->account); // 在线则更新缓存
            if (accOpt) {
                auto& accSptr = *accOpt;
                if (p->flag & ALIAS) { accSptr->alias(std::move(p->alias)); }
                if (p->flag & IMAGE) { accSptr->image(std::move(p->image)); }
                if (p->flag & PASSWORD) { accSptr->password(std::move(p->password)); }
                if (p->flag & ROLE) { accSptr->role() = std::move(p->role); }
                if (p->flag & PMS) { accSptr->pms() = std::move(p->pms); }
                if (p->flag & REPEAT) { accSptr->repeat(p->repeat); }
                if (p->flag & RATE) { accSptr->rate(p->rate); }
                if (p->flag & TEAM) { accSptr->team(p->team); }
                if (p->flag & TEAM_TYPE) { accSptr->teamType(p->teamType); }
                if (p->flag & SEX) { accSptr->sex((uint8_t)p->sex); }
            }

            api->sendHead(pub::err::kSuccess);
        };

        Main::post(std::move(funcMain));
    };

    Main::tpool->push(std::move(funcDb));
}

// 删除帐号
void ApiHandle::accountDelete(uptr_t&& uptr) {
    struct Info : public InfoBase {
        using InfoBase::InfoBase;
        Account::sptr_t accSptr;
        std::vector<string_t> accounts;
    };

    rapidjson::Document::MemberIterator it;

    // Step: 必要参数
    if ((it = doc.FindMember("token")) == doc.MemberEnd() || it->value.GetType() != rapidjson::kNumberType) {
        ct::Out::debug("Error --> account delete, token.\n");
        return sendHead(pub::err::kParams);
    }
    uint64_t token = it->value.GetUint64();
    doc.RemoveMember(it);

    if ((it = doc.FindMember("list")) == doc.MemberEnd() || it->value.GetType() != rapidjson::kArrayType) {
        ct::Out::debug("Error --> account delete, list.\n");
        return sendHead(pub::err::kParams);
    }
    auto arr = it->value.GetArray();
    if (!arr.Size()) { // 0记录直接成功
        ct::Out::debug("Error --> account delete, list size 0.\n");
        return sendHead(pub::err::kSuccess);
    }

    auto p = std::make_unique<Info>(std::forward<uptr_t>(uptr));
    auto& accounts = p->accounts;
    accounts.reserve(arr.Size()); // 提前申请空间防止被析构

    for (auto& itr : arr) {
        if (itr.GetType() == rapidjson::kStringType) {
            p->accounts.emplace_back(itr.GetString(), itr.GetStringLength());
        }
    }
    
    if (!accounts.size()) {
        ct::Out::debug("Error --> account delete, target size 0.\n");
        return sendHead(pub::err::kSuccess);
    }

    // Step: 校验权限
    auto tokenOpt = Token::find(token);
    if (!tokenOpt) {
        return sendHead(pub::err::kToken);
    }
    auto& accSptr = (*tokenOpt)->accountSptr();

    if (!accSptr->pmsExist(Main::kPmsNameAccDelete)) {
        return sendHead(pub::err::kPermission);
    }

    p->accSptr = accSptr;
    
    //p->req = std::move(req);

    // 数据库操作函数(任务线程)
    auto funcDb = [p = std::move(p)] () mutable {
        char buf[100];
        int len;

        auto db = Main::dbpool->grab();
        mysqlpp::Transaction trans(*db);
        auto query = db->query();

        for (auto& itr : p->accounts) { // 循环执行
            len = sprintf(buf, "delete from `%s` where `account`='%s' and `disable`=1 and !(`sys` & %hhu);",
                              Main::kTableNameAccount, itr.c_str(), (uint8_t)protect::kNoDel);
            auto res = query.execute(buf, len);
            ct::Out::debug("sql:%s, rows:%d.\n", buf, (int)res.rows());

            if (!res || res.rows() != 1) {
                ct::Out::flag(kErr, "Error --> Delete account, err(%d)(%s), sql(%s).\n",
                              query.errnum(), query.error(), buf);
                return Main::post([p = std::move(p)] { p->api->sendHead(pub::err::kNotFound); });
            }
        }

        trans.commit();

        auto funcMain = [p = std::move(p)] {
            auto& api = p->api;
            auto& accSptr = p->accSptr;
            
            pub::PbOpt pb; // 日志
            pb.set_account(accSptr->name());
            pb.set_time((uint32_t)time(nullptr));
            pb.set_opt(api->opt());
            pb.set_team(accSptr->team());
            for (auto& itr : p->accounts) {
                pb.add_target_name(itr);
            }
            Main::Main::sendLog(pb, pub::log::kOpt);

            api->sendHead(pub::err::kSuccess); // 回复
        };

        Main::post(std::move(funcMain));
    };

    Main::tpool->push(std::move(funcDb)); // 推送到任务线程处理
}

// 停用
void ApiHandle::accountDisable(uptr_t&& uptr) {
    struct Info : public InfoBase {
        using InfoBase::InfoBase;
        Account::sptr_t accSptr;
        std::vector<string_t> accounts;
    };

    rapidjson::Document::MemberIterator it;

    // Step: 必要参数
    if ((it = doc.FindMember("token")) == doc.MemberEnd() || it->value.GetType() != rapidjson::kNumberType) {
        ct::Out::debug("Error --> account disable, token.\n");
        return sendHead(pub::err::kParams);
    }
    uint64_t token = it->value.GetUint64();
    doc.RemoveMember(it);

    if ((it = doc.FindMember("list")) == doc.MemberEnd() || it->value.GetType() != rapidjson::kArrayType) {
        ct::Out::debug("Error --> account enable, list.\n");
        return sendHead(pub::err::kParams);
    }
    auto arr = it->value.GetArray();
    if (!arr.Size()) { // 0记录直接成功
        ct::Out::debug("Error --> account enable, list size.\n");
        return sendHead(pub::err::kSuccess);
    }
    auto p = std::make_unique<Info>(std::forward<uptr_t>(uptr));
    auto& accounts = p->accounts;
    accounts.reserve(arr.Size()); // 提前申请空间防止扩容导致复制对象

    for (auto& itr : arr) {
        if (itr.GetType() == rapidjson::kStringType) {
            accounts.emplace_back(itr.GetString(), itr.GetStringLength());
        }
    }

    if (!accounts.size()) {
        ct::Out::debug("Error --> account enable, target size 0.\n");
        return sendHead(pub::err::kSuccess);
    }

    // Step: 校验权限
    auto tokenOpt = Token::find(token);
    if (!tokenOpt) {
        return sendHead(pub::err::kToken);
    }
    auto& accSptr = (*tokenOpt)->accountSptr();

    if (!accSptr->pmsExist(Main::kPmsNameAccWrite)) {
        return sendHead(pub::err::kPermission);
    }

    //p->req = std::move(req);
    
    p->accSptr = accSptr;

    // 数据库操作函数(任务线程)
    auto funcDb = [p = std::move(p)] () mutable {
        char buf[1024];
        int len;

        auto db = Main::dbpool->grab();
        mysqlpp::Transaction trans(*db);
        auto query = db->query();

        for (auto& itr : p->accounts) { // 循环执行
            len = sprintf(buf, "update `%s` set `disable`=1 where `account`='%s' and `disable`=0 and !(`sys` & %hhu);",
                              Main::kTableNameAccount, itr.c_str(), (uint8_t)protect::kNoDisable);
            auto res = query.execute(buf, len);
            ct::Out::debug("sql:%s, rows:%d.\n", buf, (int)res.rows());

            if (!res || res.rows() != 1) {
                ct::Out::flag(kErr, "Error --> Disable account, err(%d)(%s), sql(%s)\n",
                              query.errnum(), query.error(), buf);
                return Main::post([p = std::move(p)] { p->api->sendHead(pub::err::kNotFound); });
            }
        }

        trans.commit();

        // 主线程处理节点, 强制下线
        auto funcMain = [p = std::move(p)] {
            auto& api= p->api;
            auto& accSptr = p->accSptr;
            uint32_t timesec = (uint32_t)time(nullptr);

            // 处理日志并强制下线在线帐号
            pub::PbOpt pb;
            pb.set_account(accSptr->name());
            pb.set_time(timesec);
            pb.set_opt(api->opt());
            pb.set_team(accSptr->team());

            for (auto& itr : p->accounts) { // 循环执行处理缓存
                pb.add_target_name(itr);
                auto accOpt = Account::find(itr);
                
                if (accOpt) { // 在线
                    (*accOpt)->tokenDown(pub::off::kMandatory, timesec);
                }
            }

            Main::sendLog(pb, pub::log::kOpt);
            api->sendHead(pub::err::kSuccess);
        };

        Main::post(std::move(funcMain));
    };

    Main::tpool->push(std::move(funcDb)); // 推送到任务线程处理
}

// 启用
void ApiHandle::accountEnable(uptr_t&& uptr) {
    struct Info : public InfoBase {
        using InfoBase::InfoBase;
        Account::sptr_t accSptr;
        std::vector<string_t> accounts;
    };

    rapidjson::Document::MemberIterator it;

    // Step: 必要参数
    if ((it = doc.FindMember("token")) == doc.MemberEnd() || it->value.GetType() != rapidjson::kNumberType) {
        ct::Out::debug("Error --> account enable, token.\n");
        return sendHead(pub::err::kParams);
    }
    uint64_t token = it->value.GetUint64();
    doc.RemoveMember(it);

    if ((it = doc.FindMember("list")) == doc.MemberEnd() || it->value.GetType() != rapidjson::kArrayType) {
        ct::Out::debug("Error --> account enable, list.\n");
        return sendHead(pub::err::kParams);
    }
    auto arr = it->value.GetArray();
    if (!arr.Size()) { // 0记录直接成功
        ct::Out::debug("Error --> account enable, list size.\n");
        return sendHead(pub::err::kSuccess);
    }

    auto p = std::make_unique<Info>(std::forward<uptr_t>(uptr));
    auto& accounts = p->accounts;
    accounts.reserve(arr.Size()); // 提前申请空间防止扩容导致复制对象

    for (auto& itr : arr) {
        if (itr.GetType() == rapidjson::kStringType) {
            accounts.emplace_back(itr.GetString(), itr.GetStringLength());
        }
    }

    if (!accounts.size()) {
        ct::Out::debug("Error --> account enable, target size 0.\n");
        return sendHead(pub::err::kSuccess);
    }

    // Step: 校验权限
    auto tokenOpt = Token::find(token);
    if (!tokenOpt) {
        return sendHead(pub::err::kToken);
    }
    auto& accSptr = (*tokenOpt)->accountSptr();

    if (!accSptr->pmsExist(Main::kPmsNameAccWrite)) {
        return sendHead(pub::err::kPermission);
    }

    p->accSptr = accSptr;

    // 数据库操作函数(任务线程)
    auto funcDb = [p = std::move(p)] () mutable {
        char buf[1024];
        int len;

        auto db = Main::dbpool->grab();
        mysqlpp::Transaction trans(*db);
        auto query = db->query();

        for (auto& itr : p->accounts) { // 循环执行
            len = sprintf(buf, "update `%s` set `disable`=0 where `account`='%s' and `disable`=1;",
                              Main::kTableNameAccount, itr.c_str());
            auto res = query.execute(buf, len);
            ct::Out::debug("sql:%s, rows:%d.\n", buf, (int)res.rows());

            if (!res || res.rows() != 1) {
                ct::Out::flag(kErr, "Error --> Enable account, err(%d)(%s), sql(%s).\n",
                              query.errnum(), query.error(), buf);
                return Main::post([p = std::move(p)] { p->api->sendHead(pub::err::kNotFound); });
            }
        }

        trans.commit();

        auto funcMain = [p = std::move(p)] {
            auto& api= p->api; // 日志
            auto& accSptr = p->accSptr;
            pub::PbOpt pb;
            pb.set_account(accSptr->name());
            pb.set_time((uint32_t)time(nullptr));
            pb.set_opt(api->opt());
            pb.set_team(accSptr->team());

            for (auto& itr : p->accounts) {
                pb.add_target_name(itr);
            }

            Main::sendLog(pb, pub::log::kOpt);
            api->sendHead(pub::err::kSuccess);
        };

        Main::post(std::move(funcMain));
    };

    Main::tpool->push(std::move(funcDb)); // 推送到任务线程处理
}

// 重置密码
void ApiHandle::accountResetPassword(uptr_t&& uptr) {
    struct Info : public InfoBase {
        using InfoBase::InfoBase;
        string_t account, password;
    };

    rapidjson::Document::MemberIterator it;

    if ((it = doc.FindMember("secretKey")) == doc.MemberEnd() || it->value.GetType() != rapidjson::kStringType) {
        ct::Out::debug("Error --> account resetpwd, secretKey.\n");
        return sendHead(pub::err::kParams);
    }
    if (Main::secret.compare(it->value.GetString()) != 0) { // 密钥不对
        return sendHead(pub::err::kPermission);
    }
    doc.RemoveMember(it);

    if ((it = doc.FindMember("account")) == doc.MemberEnd() || it->value.GetType() != rapidjson::kStringType) { 
        ct::Out::debug("Error --> account resetpwd, account.\n");
        return sendHead(pub::err::kParams);
    }

    auto p = std::make_unique<Info>(std::forward<uptr_t>(uptr));
    p->account.assign(it->value.GetString(), it->value.GetStringLength());
    doc.RemoveMember(it);

    if ((it = doc.FindMember("password")) == doc.MemberEnd() || it->value.GetType() != rapidjson::kStringType
        || it->value.GetStringLength() != 32) {
        ct::Out::debug("Error --> account resetpwd, password.\n");
        return sendHead(pub::err::kParams);
    }
    p->password.assign(it->value.GetString(), it->value.GetStringLength());

    auto funcDb = [p = std::move(p)] () mutable {
        char buf[1024];
        int len = sprintf(buf, "update `%s` set `password`='%s',`updateTime`=now() where `account`='%s' "\
                          "and `disable`=0 and !(`sys` & %hhu)", 
                          Main::kTableNameAccount, p->password.c_str(), p->account.c_str(), protect::kNoMod);

        auto db = Main::dbpool->grab();
        auto query = db->query();
        auto res = query.execute(buf, len);

        ct::Out::debug("sql:%s, rows:%d.\n", buf, (int)res.rows());

        if (!res || res.rows() != 1) {
            ct::Out::flag(kErr, "Error --> Update account db, err(%d)(%s), sql(%s).\n",
                          query.errnum(), query.error(), buf);
            return Main::post([p = std::move(p)] { p->api->sendHead(pub::err::kNotFound); });
        }

        // 主线程操作函数, 节点在线则更新缓存, 回复成功
        auto funcMain = [p = std::move(p)] {
            auto& api= p->api;

            auto accOpt = Account::find(p->account);
            if (accOpt) { // 在线
                (*accOpt)->password(std::move(p->password));
            }

            pub::PbOpt pb;
            pb.set_account(Main::kSystemName);
            pb.set_time((uint32_t)time(nullptr));
            pb.set_opt(api->opt());
            pb.set_team(1);
            pb.add_target_name(p->account);

            Main::sendLog(pb, pub::log::kOpt);
            api->sendHead(pub::err::kSuccess);
        };

        Main::post(std::move(funcMain));
    };

    Main::tpool->push(std::move(funcDb));
}

// 注册帐号
void ApiHandle::accountRegiste(uptr_t&& uptr) { 
    struct Info : public InfoBase {
        using InfoBase::InfoBase;
        string_t account, password, alias, phone, image, describe;
        int sex, type, repeat;
    };

    rapidjson::Document::MemberIterator it;
    auto p = std::make_unique<Info>(std::forward<uptr_t>(uptr));

    if ((it = doc.FindMember("account")) == doc.MemberEnd() || it->value.GetType() != rapidjson::kStringType
        || !ct::StringUtil::allow(it->value.GetString(), "-_", it->value.GetStringLength(), 2)) { // 只允许字母数字,下划线中划线
        ct::Out::debug("Error --> account %s invalid.\n", it->value.GetString());
        return sendHead(pub::err::kParams);
    }
    p->account.assign(it->value.GetString(), it->value.GetStringLength());
    doc.RemoveMember(it);

    if ((it = doc.FindMember("type")) == doc.MemberEnd() || it->value.GetType() != rapidjson::kNumberType
        || (p->type =it->value.GetInt()) < (int)Account::Type::JCG
        || p->type > (int)Account::Type::UNIONID) {
        ct::Out::debug("Error --> account region, type(%hhu).\n", p->type);
        return sendHead(pub::err::kParams);
    }
    doc.RemoveMember(it);

    if (p->type == (uint8_t)Account::Type::JCG) { // JCG帐号必须有密码
        if (p->account.size() > Account::kNameMaxLen) {
            return sendHead(pub::err::kParams);
        }
        if ((it = doc.FindMember("password")) == doc.MemberEnd() || it->value.GetType() != rapidjson::kStringType
            || it->value.GetStringLength() != 32) {
            ct::Out::debug("Error --> account region, password.\n");
            return sendHead(pub::err::kParams);
        }
        p->password.assign(it->value.GetString(), it->value.GetStringLength());
        doc.RemoveMember(it);
        p->repeat = 0; // JCG帐号默认不允许重复登陆

    } else {
        /*if (p->account.size() != 32) {
            return sendHead(pub::err::kParams);
        }*/
        p->repeat = 1; // 微信帐号允许重复登陆
    }

    // Step: 可选参数
    if ((it = doc.FindMember("alias")) != doc.MemberEnd() && it->value.GetType() == rapidjson::kStringType) {
        if (it->value.GetStringLength() > Account::kAliasMaxLen) {
            ct::Out::debug("Error --> account region, alias.\n");
            return sendHead(pub::err::kParams);
        }
        p->alias.assign(it->value.GetString(), it->value.GetStringLength());
        doc.RemoveMember(it);
    }
    if ((it = doc.FindMember("phone")) != doc.MemberEnd() && it->value.GetType() == rapidjson::kStringType) {
        if (it->value.GetStringLength() > Account::kPhoneMaxLen) {
            ct::Out::debug("Error --> account region, phone.\n");
            return sendHead(pub::err::kParams);
        }
        p->phone.assign(it->value.GetString(), it->value.GetStringLength());
        doc.RemoveMember(it);
    }
    if ((it = doc.FindMember("image")) != doc.MemberEnd() && it->value.GetType() == rapidjson::kStringType) {
        if (it->value.GetStringLength() > Account::kImageMaxLen) {
            ct::Out::debug("Error --> account region, image.\n");
            return sendHead(pub::err::kParams);
        }
        p->image.assign(it->value.GetString(), it->value.GetStringLength());
        doc.RemoveMember(it);
    }
    if ((it = doc.FindMember("describe")) != doc.MemberEnd() && it->value.GetType() == rapidjson::kStringType) {
        if (it->value.GetStringLength() > Account::kDescMaxLen) {
            ct::Out::debug("Error --> account region, describe.\n");
            return sendHead(pub::err::kParams);
        }
        p->describe.assign(it->value.GetString(), it->value.GetStringLength());
        doc.RemoveMember(it);
    }
    if ((it = doc.FindMember("sex")) != doc.MemberEnd() && it->value.GetType() == rapidjson::kNumberType) {
        if ((p->sex = it->value.GetInt()) < 0 || p->sex > 2) {
            ct::Out::debug("Error --> account region, sex(%d).\n", p->sex);
            return sendHead(pub::err::kParams);
        }
        doc.RemoveMember(it);
    }

    //p->req = std::move(req);
    

    auto funcDb = [p = std::move(p)] () mutable {
        char buf[4096], alias[100], phone[100], image[400], describe[400];

        // 插入库， team固定0，rate固定为1，创建者帐号固定为system
        int len = sprintf(buf, "insert into `%s` (`account`,`type`,`repeat`,`sex`,`teamType`,`team`,`rate`,`alias`,"\
                          "`createAccount`,`password`,`phone`,`image`,`describe`,`role`) "\
                          "values('%s',%d,%d,%d,%d,0,1,'%s','system','%s','%s','%s','%s','%s');",
                          Main::kTableNameAccount, p->account.c_str(), 
                          p->type, p->repeat, p->sex, (int)Account::TeamType::kUser,
                          ct::StringUtil::encodeSqlPtr(p->alias.c_str(), alias),
                          p->password.c_str(),
                          ct::StringUtil::encodeSqlPtr(p->phone.c_str(), phone),
                          ct::StringUtil::encodeSqlPtr(p->image.c_str(), image),
                          ct::StringUtil::encodeSqlPtr(p->describe.c_str(), describe),
                          "[1]"
        );

        auto db = Main::dbpool->grab();
        auto query = db->query();
        auto res = query.execute(buf, len);
         
        ct::Out::debug("sql:%s, rows:%d, info:%s.\n", buf, (int)res.rows(), res.info()); // 帐号没有自增ID主键，拿不到insert_id

        if (!res || res.rows() != 1) {
            ct::Out::flag(kErr, "Error --> Registe account, err(%d)(%s), sql(%s).\n",
                          query.errnum(), query.error(), buf);
            // 回复错误，1062为重复主键错误，表示重名了
            return Main::post([p = std::move(p), 
                    err = query.errnum() == 1062 ? pub::err::kExist : pub::err::kInternal] { 
                p->api->sendHead(err); 
            });
        }

        auto funcMain = [p = std::move(p)] {
            auto& api= p->api; // 日志
            std::string remark;
            remark.reserve(2048);
            char tmp[4096];
            int ret;

            if (!p->alias.empty()) { // p里面name和alias已经被move到pmsSptr里了
                remark += "(alias:" + p->alias + ")";
            }
            if (!p->phone.empty()) {
                remark += "(phone:" + p->phone + ")";
            }
            if (!p->image.empty()) {
                remark += "(image:" + p->image + ")";
            }
            ret = sprintf(tmp, "(repeat:%u)(sex:%u)", p->repeat, p->sex);
            remark.append(tmp, ret);
            if (!p->describe.empty()) {
                remark += "(desc:" + p->describe + ")";
            }

            pub::PbOpt pb;
            pb.set_account(Main::kSystemName);
            pb.set_time((uint32_t)time(nullptr));
            pb.set_opt(api->opt());
            pb.set_team(1);
            pb.set_remark(remark);
            pb.add_target_name(p->account);
            Main::sendLog(pb, pub::log::kOpt);
            api->sendHead(pub::err::kSuccess); // 回复
        };

        Main::post(std::move(funcMain));
    };
    
    Main::tpool->push(std::move(funcDb));
}

// 登陆
void ApiHandle::accountLogin(uptr_t&& uptr) {
    struct Info : public InfoBase {
        using InfoBase::InfoBase;
        uint32_t timeout { Main::timeout }; // 默认token超时时间为全局配置中指定
        int type;
        string_t account, password, ip;
    };

    auto p = std::make_unique<Info>(std::forward<uptr_t>(uptr));
    rapidjson::Document::MemberIterator it;

    // 帐号(必须)
    if ((it = doc.FindMember("account")) == doc.MemberEnd() || it->value.GetType() != rapidjson::kStringType) {
        ct::Out::debug("Error --> account login, account.\n");
        return sendHead(pub::err::kParams);
    }
    p->account.assign(it->value.GetString(), it->value.GetStringLength());
    doc.RemoveMember(it); // 存值后删除

    // ip(必须)
    if ((it = doc.FindMember("ip")) == doc.MemberEnd() || it->value.GetType() != rapidjson::kStringType) {
        ct::Out::debug("Error --> account login, ip.\n");
        return sendHead(pub::err::kParams);
    }
    p->ip.assign(it->value.GetString(), it->value.GetStringLength());
    doc.RemoveMember(it); // 存值后删除

    // type(必须)
    if ((it = doc.FindMember("type")) == doc.MemberEnd() || it->value.GetType() != rapidjson::kNumberType) {
        ct::Out::debug("Error --> account login, type.\n");
        return sendHead(pub::err::kParams);
    }
    p->type = it->value.GetInt();
    doc.RemoveMember(it); // 存值后删除

    // timeout(可选)
    if ((it = doc.FindMember("timeout")) != doc.MemberEnd() && it->value.GetType() == rapidjson::kNumberType) {
        int ret = it->value.GetInt();
        if (ret < 1 || ret > 600) { // 范围必须在1-600之间
            ct::Out::debug("Error --> account login, timeout.\n");
            return sendHead(pub::err::kParams);
        }
        p->timeout = (uint32_t)ret * 60;
        doc.RemoveMember(it); // 存值后删除
    }

    // password(可选), 注册帐号必须要有密码
    if (p->type == (int)Account::Type::JCG) { // 
        if ((it = doc.FindMember("password")) == doc.MemberEnd() || it->value.GetType() != rapidjson::kStringType
            || it->value.GetStringLength() != 32) {
            ct::Out::debug("Error --> account login, password.\n");
            return sendHead(pub::err::kParams);
        }
        p->password.assign(it->value.GetString(), it->value.GetStringLength());

    } else if (p->type != (int)Account::Type::UNIONID) {
        return sendHead(pub::err::kParams);
    }

    // 回复函数, 检查重复登陆, 生成token, 构造协议数据并回复
    auto funcResp = [] (const Account::sptr_t& acc, std::unique_ptr<Info>&& p) {
        // 生成新的token
        uint64_t token = rand(); // 随机值
        uint32_t timesec = (uint32_t)time(nullptr); // 时间

        if (!acc->repeat()) { // 该帐号不允许重复登陆
            ct::Out::debug("Account login, '%s' no repeat, remove old.\n", acc->name().c_str());
            acc->tokenDown(pub::off::kReload, timesec); // 移除之前在线的token
        }

        auto tokenSptr = std::make_shared<Token>(acc, token, timesec, p->timeout, std::move(p->ip));

        do {
            if (Token::all.emplace(std::piecewise_construct, std::forward_as_tuple(token),
                           std::forward_as_tuple(tokenSptr)).second) { // 加入全局token列表, 大多情况成功
                break;
            }

            // 插入失败,token生成的随机值相同(极小几率)
            token += timesec;
            tokenSptr->token(token);
        } while (1);
        
        auto& api= p->api;
        rapidjson::Writer<buffer_t> w(api->buf);
        api->setHead((uint8_t)pub::err::kSuccess); // 协议头
        accountInfoToJson(w, acc, token);
        api->session->sendReponse(std::move(api->buf)); // 发送
    };

    // 查找帐号当前是否在线
    auto accOpt = Account::find(p->account);

    if (accOpt) { // 找到, 帐号当前在线
        auto& accSptr = *accOpt;
        if (p->type != (int)accSptr->type() || 
            (p->type == (int)Account::Type::JCG && accSptr->password().compare(p->password))) { // 密码对不上
            return sendHead(pub::err::kAccPwd);
        }
        return funcResp(accSptr, std::move(p));
    }

    // 执行到此, 表示需要读取数据库, 线程池中执行函数, 加上mutable关键字, 防止const后续还会将account, password右值转移走, 提高效率
    auto funcDb = [funcResp, p = std::move(p)] () mutable {
        auto db = Main::dbpool->grab();
        char buf[4096];
        enum Idx {
            SYS,
            REPEAT,
            SEX,
            TEAM_TYPE,
            TEAM,
            RATE,
            ROLE,
            PMS,
            ALIAS,
            IMAGE,
            CHILD_TEAMS,
        };

        // 早期版本不查子孙分组
         /* len = sprintf(buf, "select `sys`,`repeat`,`sex`,`teamType`,`team`,`rate`,`role`,`pms`,`alias`,`image` "\
                      "from `%s` where `account`='%s' and `type`=%d and `disable`=0",
                      Main::kTableNameAccount, p->account.c_str(), p->type); */
        //auto start = std::chrono::system_clock::now();
        mysqlpp::Query query = db->query();
        
        // 开头 WITH RECURSIVE
        query << "WITH RECURSIVE sub_teams AS ("
            << "SELECT t.id, t.teamId, a.id AS account_id "
            << "FROM `" << Main::kTableNameAccount << "` a "
            << "JOIN `" << Main::kTableNameTeam << "` t ON a.team = t.id "
            << "WHERE a.account = " << mysqlpp::quote << p->account
            << " AND a.type = " << p->type
            << " AND a.disable = 0 "
            << " AND a.team <> 1";

        if (p->type == static_cast<int>(Account::Type::JCG)) { // 添加密码条件
            query << " AND a.`password` = " << mysqlpp::quote << p->password;
        }

        query << " UNION ALL "
            << "SELECT t.id, t.teamId, st.account_id "
            << "FROM `" << Main::kTableNameTeam << "` t "
            << "INNER JOIN sub_teams st ON t.teamId = st.id "
            << ") ";

        // 主查询
        query << "SELECT a.sys, a.repeat, a.sex, a.teamType, a.team, "
            << "a.rate, a.role, a.pms, a.alias, a.image, "
            << "GROUP_CONCAT(st.id ORDER BY st.id) AS sub_team_ids "
            << "FROM `" << Main::kTableNameAccount << "` a "
            << "LEFT JOIN sub_teams st ON st.account_id = a.id "
            << "WHERE a.account = " << mysqlpp::quote << p->account
            << " AND a.type = " << p->type
            << " AND a.disable = 0";

      // 再次添加密码条件
        if (p->type == static_cast<int>(Account::Type::JCG)) {
            query << " AND a.`password` = " << mysqlpp::quote << p->password;
        }

        query << " GROUP BY a.id;";
        //std::cout << "SQL = " << query.str() << std::endl; 
        auto res = query.store();
        //auto end = std::chrono::system_clock::now();
        //std::cout << "timespeed:" << std::chrono::duration(end-start).count() << std::endl;

        if (!res) { // 数据库读取错误
            ct::Out::flag(kErr, "Error --> Account login, err(%d)(%s), sql(%s).\n", 
                          query.errnum(), query.error(), buf);
            return Main::post([p = std::move(p)] { p->api->sendHead(pub::err::kInternal); });
        }

        if (res.num_rows() == 0) { // 没有记录, 帐号不存在(停用)或密码不对
            ct::Out::flag(kErr, "Error --> Account login nodata, sql(%s).\n", buf);
            return Main::post([p = std::move(p)] { p->api->sendHead(pub::err::kAccPwd); });
        }

        auto& row = res[0]; // 只会有一条记录

        auto accSptr = std::make_shared<Account>( // 构造帐号节点智能指针
                std::move(p->account), std::move(p->password),
                string_t(row[ALIAS].data(), row[ALIAS].size()),
                string_t(row[IMAGE].data(), row[IMAGE].size()),
                row[ROLE].data(), row[PMS].data(), (uint8_t)row[TEAM_TYPE],
                (uint32_t)row[TEAM], (uint32_t)row[RATE], (bool)row[REPEAT],
                (uint8_t)row[SYS], (uint8_t)row[SEX], (uint8_t)p->type
        );

        if (!row[CHILD_TEAMS].is_null()) { // 格式为 3，4，5
            strncpy(buf, row[CHILD_TEAMS].data(), row[CHILD_TEAMS].size());
            buf[row[CHILD_TEAMS].size()] = '\0';  // 确保字符串以null结尾
            char* save_ptr = nullptr;
            char* token = strtok_r(buf, ",", &save_ptr);
            while (token != nullptr) {
                accSptr->addChildTeam((uint32_t)std::atoi(token));
                token = strtok_r(nullptr, ",", &save_ptr);
            }
        }

        // 读取成功, 只会有一条记录
        // 成功时推送到主线程后执行后续
        auto funcMain = [funcResp, accSptr = std::move(accSptr), p = std::move(p)] () mutable {
            // 全局列表加入weak_ptr，由于多线程并发, 这里有可能已存在了
            auto ret = Account::all.emplace(std::piecewise_construct,
                                std::forward_as_tuple(accSptr->name()), std::forward_as_tuple(accSptr));

            if (!ret.second) { // 已存在, 这里更新shared_ptr为之前的上线的, 不替换map中值为本次的(之前的token没有处理)
                ct::Out::debug("account %s in map, replace it.\n");
                accSptr = ret.first->second.lock();
            }

            funcResp(accSptr, std::move(p)); // 发送回复
        };

        Main::post(std::move(funcMain)); // 推到主线程处理节点
    };

    // 新线程中处理数据库操作
    Main::tpool->push(std::move(funcDb)); // 线程池中处理
}

// 登出
void ApiHandle::accountLogout(uptr_t&& uptr) {
    rapidjson::Document::MemberIterator it;

    if ((it = doc.FindMember("token")) == doc.MemberEnd() || it->value.GetType() != rapidjson::kNumberType) {
        return sendHead(pub::err::kParams);
    }
    uint64_t token = it->value.GetUint64();
    doc.RemoveMember(it); // 存值后删除

    auto tokenOpt = Token::find(token);
    if (!tokenOpt) {
        return sendHead(pub::err::kToken);
    }

    auto& tokenSptr = *tokenOpt;
    auto& accSptr = tokenSptr->accountSptr(); // 权限校验
    uint32_t timesec = (uint32_t)time(nullptr); // 

    // 下线指定的token
    if ((it = doc.FindMember("tokenList")) != doc.MemberEnd() && it->value.GetType() == rapidjson::kArrayType) {
        if (!accSptr->pmsExist(Main::kPmsNameAccWrite)) {
            return sendHead(pub::err::kPermission);
        }
        auto arr = it->value.GetArray();
        std::set<Token*> tokens; // 存有效指针, 只能单线程模型

        for (auto& itr : arr) {
            if (itr.GetType() != rapidjson::kNumberType) {
                continue;
            }
            token = itr.GetUint64();
            auto tokenOpt = Token::find(token);
            if (!tokenOpt) {
                continue;
            }
            if ((*tokenOpt)->accountSptr()->sys() & protect::kNoMod) { // 不可修改
                continue;
            }
            tokens.insert(tokenOpt->get());
        }

        for (auto& itr : tokens) {
            if (itr != tokenSptr.get()) { // 非自身
                pub::PbOpt pb;  // 发送操作日志
                pb.set_account(accSptr->name());
                pb.set_time(timesec);
                pb.set_opt(this->opt());
                pb.set_team(accSptr->team());
                pb.add_target_name(itr->accountSptr()->name());
                Main::sendLog(pb, pub::log::kOpt);
                itr->logoutCaugh(pub::off::kMandatory); // 下线原因为强制
            
            } else { // 自身则下线原因为登出
                itr->logoutCaugh(pub::off::kLogout);
            }

            // 强制下线, 包含自己, 析构时会发送上下线日志
            itr->lastTime(timesec); // 更新时间, 后作为下线时间
            Token::tokenDel(itr->token()); // 如果是自己, 目前还有引用计数, 函数返回后才执行销毁
        }

    } else if ((it = doc.FindMember("accountList")) != doc.MemberEnd() && it->value.GetType() == rapidjson::kArrayType) {
        if (!accSptr->pmsExist(Main::kPmsNameAccWrite)) {
            return sendHead(pub::err::kPermission);
        }
        auto arr = it->value.GetArray();
        std::set<Account*> accountList; // 存有效指针, 只能单线程模型
        string_t account;

        for (auto& itr : arr) {
            if (itr.GetType() != rapidjson::kStringType) {
                continue;
            }
            account.assign(itr.GetString(), itr.GetStringLength());
            auto accOpt = Account::find(account);
            if (!accOpt) {
                continue;
            }
            if ((*accOpt)->sys() & protect::kNoMod) { // 不可修改
                continue;
            }
            accountList.insert(accOpt->get());
        }
        
        for (auto& itr : accountList) {
            // 操作日志, 登出别的帐号或当前帐号不止一个token才记录操作日志
            if (itr != tokenSptr->accountSptr().get() || itr->tokens().size() > 1) { 
                pub::PbOpt pb;
                pb.set_account(accSptr->name());
                pb.set_time(timesec);
                pb.set_opt(this->opt());
                pb.set_team(accSptr->team());
                pb.add_target_name(itr->name());
                Main::sendLog(pb, pub::log::kOpt);
            }

            // 强制下线, 包含自己
            itr->tokenDown(pub::off::kMandatory, timesec); // 下线目标帐号所有token
        }
    
    } else { // 下线自己
        tokenSptr->lastTime(timesec);
        tokenSptr->logoutCaugh(pub::off::kLogout);
        Token::tokenDel(token);
    }

#if 0
    if (account.empty() || accSptr->name() == account) { // 登出当前

    } else { // 登出指定帐号
        if (!accSptr->pmsExist(Main::kPmsNameAccWrite)) {
            return sendHead(pub::err::kPermission);
        }
        auto targetAccOpt = Account::find(account); // 查找目标帐号
        if (!targetAccOpt) {
            return sendHead(pub::err::kNotFound);
        }
        auto& targetAccSptr = *targetAccOpt;

        pub::PbOpt pb;
        pb.set_account(accSptr->name());
        pb.set_time(timesec);
        pb.set_opt(api->opt());
        pb.set_team(accSptr->team());
        pb.add_target_name(targetAccSptr->name());
        Main::sendLog(pb, pub::log::kOpt);

        targetAccSptr->tokenDown(pub::off::kMandatory, timesec); // 下线目标帐号所有token
    }
#endif

    return sendHead(pub::err::kSuccess);
}

// token检查
void ApiHandle::accountTokenCheck(uptr_t&& uptr) {
    rapidjson::Document::MemberIterator it;

    if ((it = doc.FindMember("token")) == doc.MemberEnd() || it->value.GetType() != rapidjson::kNumberType) {
        return sendHead(pub::err::kParams);
    }

    uint64_t token = it->value.GetUint64();
    auto tokenOpt = Token::find(token);
    if (!tokenOpt) {
        return sendHead(pub::err::kToken);
    }

    auto& accSptr = (*tokenOpt)->accountSptr();
    rapidjson::Writer<buffer_t> w(buf);
    setHead((uint8_t)pub::err::kSuccess); // 协议头
    accountInfoToJson(w, accSptr, token);
    session->sendReponse(std::move(buf));
}

// 检查, 内部调用
void ApiHandle::accountCheck(uptr_t&& uptr) {
    rapidjson::Document::MemberIterator it;

    if ((it = doc.FindMember("token")) == doc.MemberEnd() || it->value.GetType() != rapidjson::kNumberType) {
        return sendHead(pub::err::kParams);
    }

    uint64_t token = it->value.GetUint64();
    doc.RemoveMember(it);

    auto tokenOpt = Token::find(token);
    if (!tokenOpt) {
        return sendHead(pub::err::kToken);
    }

    auto& accSptr = (*tokenOpt)->accountSptr();

    // 权限检查, 可选发送
    if ((it = doc.FindMember("pms")) != doc.MemberEnd() && it->value.GetType() == rapidjson::kArrayType) {
        auto arr = it->value.GetArray();
        
        for (auto& itr : arr) { // 循环判断各个权限是否存在, 任意一个权限不存在则返回无权限
            if (itr.GetType() == rapidjson::kStringType
                && !accSptr->pmsExist(string_t(itr.GetString(), itr.GetStringLength()))) {
                return sendHead(pub::err::kPermission);
            }
        }
        doc.RemoveMember(it);
    }

    rapidjson::Writer<buffer_t> w(buf);
    setHead((uint8_t)pub::err::kSuccess); // 协议头

    w.StartObject();
    w.Key("account");
    w.String(accSptr->name().data(), accSptr->name().size());
    w.Key("team");
    w.Uint(accSptr->team());
    w.Key("teamType");
    w.Uint(accSptr->teamType());
    w.Key("sys");
    w.Uint(accSptr->sys());
    w.Key("rate");
    w.Uint(accSptr->rate());
    if (accSptr->team() != 1 && accSptr->childTeams().size()) {
        w.Key("teams");
        w.StartArray();
        for (auto& itr : accSptr->childTeams()) {
            w.Uint(itr);
        }
        w.EndArray();
    }
    
    // id请求，将符合条件范围的ID列表加入， 可选发送
    if ((it = doc.FindMember("id")) != doc.MemberEnd() && it->value.GetType() == rapidjson::kArrayType) {
        auto arr = it->value.GetArray();

        if (arr.Size() == 2 && arr[0].GetType() == rapidjson::kNumberType
            && arr[1].GetType() == rapidjson::kNumberType) {
            uint32_t min = arr[0].GetUint();
            uint32_t max = arr[1].GetUint();
            pms_list_t pmss;

            w.Key("id");
            w.StartArray();
            accSptr->pmsAll(pmss);
            for (auto& itr : pmss) {
                if (itr->id() >= min && itr->id() <= max) {
                    w.Uint(itr->id());
                }
            }
            w.EndArray();
        }
    }

    w.EndObject();
    session->sendReponse(std::move(buf)); //
}

// 更新分组, 后台模块内部调用, 只做更新不回复
void ApiHandle::accountNewTeam(uptr_t&& uptr) {
    rapidjson::Document::MemberIterator it;
    std::string account;

    if ((it = doc.FindMember("account")) == doc.MemberEnd() || it->value.GetType() != rapidjson::kStringType) { // 目标
        return;
    }
    account.assign(it->value.GetString(), it->value.GetStringLength());
    doc.RemoveMember(it);

    if ((it = doc.FindMember("team")) == doc.MemberEnd() || it->value.GetType() != rapidjson::kNumberType) {
        return;
    }

    uint32_t teamId = it->value.GetUint();
    auto accOpt = Account::find(account); // 在线则更新缓存
    if (accOpt) {
        auto accSptr = *accOpt;
        accSptr->team(teamId);
        accSptr->teamType((uint32_t)Account::TeamType::kUser); // 绑定时更新的分组类型固定用户分组
    }

    // 更新数据库
    auto funcDb = [acct = std::move(account), teamId] {
        char buf[4096];
        int len;

        len = sprintf(buf, "update `%s` set `teamType`=%u,`team`=%u,`updateTime`=now() where `account`='%s' and !(`sys` & %hhu);",
                               Main::kTableNameAccount, 
                               (uint32_t)Account::TeamType::kUser, 
                               teamId, acct.data(), protect::kNoMod);

        auto db = Main::dbpool->grab();
        auto query = db->query();
        auto res = query.execute(buf, len);
        ct::Out::debug("sql:%s, rows:%d.\n", buf, (int)res.rows());

        if (!res || res.rows() != 1) {
            ct::Out::flag(kErr, "Error --> Update account new team, err(%d)(%s), sql(%s).\n",
                          query.errnum(), query.error(), buf);
        }
    };

    Main::tpool->push(std::move(funcDb));
}